#pragma once

#include <iostream>
#include <string>

#include "log.hpp"

#include <cstring>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

Log log;

#define MMAP_FILE "./mmap"
#define MODE 0664

class Mmap
{
    enum Mmap_Error
    {
        MMAP_CREAT_ERROR = 1,
        MMAP_DELETE_ERROR = 2
    };
public:
    Mmap(const char* filename, size_t length)
        : _length(length)
    {
        _fd = open(filename, O_RDWR | O_CREAT, MODE);
        _addr = mmap(nullptr, _length, PROT_WRITE | PROT_READ, MAP_SHARED, _fd, 0);
        if (_addr == (void*)-1)
        {
            log(Fatal, "mmap creat error: %s", strerror(errno));
            exit(MMAP_CREAT_ERROR);
        }
    }

    void* get()
    {
        return _addr;
    }

    ~Mmap()
    {
        if (munmap(_addr, _length) < 0 || close(_fd) < 0)
        {
            log(Fatal, "mmap delete error: %s", strerror(errno));
            exit(MMAP_DELETE_ERROR);
        }
    }
private:
    int _fd;
    void* _addr;
    size_t _length;
};

#define FIFO_FILE "./fifo"

class FIFO
{
    enum Fifo_State_Error
    {
        FIFO_CREATE_ERROR = 1,
        FIFO_DELETE_ERROR = 2,
        FIFO_OPEN_ERROR = 3
    };
public:
    FIFO(const char* filename = FIFO_FILE)
        : _fifo_path(filename)
    {
        int n = mkfifo(_fifo_path.c_str(), MODE);
        if (n < 0)
        {
            log(Fatal, "fifo creat error: %s", strerror(errno));
            exit(FIFO_CREATE_ERROR);
        }
    }

    static int open_read(const char* filename)
    {
        int fd = open(filename, O_RDONLY);
        if (fd < 0)
        {
            log(Fatal, "error string: %s", strerror(errno));
            exit(FIFO_OPEN_ERROR);
        }
        return fd;
    }
    static int open_write(const char* filename)
    {
        int fd = open(filename, O_WRONLY);
        if (fd < 0)
        {
            log(Fatal, "error string: %s", strerror(errno));
            exit(FIFO_OPEN_ERROR);
        }
        return fd;
    }

    ~FIFO()
    {
        int m = unlink(_fifo_path.c_str());
        if (m == -1)
        {
            log(Fatal, "fifo unlink error %s", strerror(errno));
            exit(FIFO_DELETE_ERROR);
        }
    }
private:
    std::string _fifo_path;
};
